import { Matrix4 } from "three";
import GameApplication from "../../system/GameApplication";
import GameObject from "../gameObject/GameObject";
import Geo from "../gameObject/Geo";
import Mat from "../gameObject/Mat";

interface MeshData {
    mat: Mat,
    geo: Geo,
}

export default class Mesh extends GameObject {
    public geo: Geo;
    public mat: Mat;

    public parent: GameObject | null = null;
    public children: GameObject[] = [];

    public depthTest: boolean;
    public isLD: boolean = false;

    public gl: any = GameApplication.app.gl;
    public pvM4: Matrix4 = GameApplication.getCamera()?.getPvM4()!;

    public constructor(name: string, attr: MeshData) {
        super(name);

        this.geo = attr.geo
        this.mat = attr.mat
        this.depthTest = true;

    }

    public init(gl: any) {
        const { mat, geo } = this;
        geo && geo.init(gl);
        mat && mat.init(gl);
    }

    public setUniform(name: string, uniform: any) {
        const { mat, children } = this;
        mat && mat.setData(name, uniform);

        for (let obj of children) {
            const mesh = obj as unknown as Mesh;
            if (mesh instanceof Mesh) {
                mesh.setUniform(name, uniform);
            }
        }
    }

    public setMap(name: string, texture: any) {
        const { mat, children } = this;
        mat && mat.setMap(name, texture);

        for (let obj of children) {
            const mesh = obj as unknown as Mesh;
            if (mesh instanceof Mesh) {
                mesh.setUniform(name, texture);
            }
        }
    }

    public setAttrib(name: string, attrib: any) {
        const { geo, children } = this;
        geo && geo.setData(name, attrib);

        for (let obj of children) {
            const mesh = obj as unknown as Mesh;
            if (mesh instanceof Mesh) {
                mesh.setAttrib(name, attrib);
            }
        }
    }

    public updateData(gl: any, attribs: any, uniforms: any) {
        const { mat, geo, children } = this;
        mat && mat.update(gl, uniforms);
        geo && geo.update(gl, attribs);
    }

    public draw(gl: any) {
        const { depthTest, mat, geo } = this;
        const { mode } = mat as any;
        const { drawType, count } = geo!;

        depthTest ? gl.enable(gl.DEPTH_TEST) : gl.disable(gl.DEPTH_TEST);

        if (!(mat == null || geo == null)) {
            for (let type of mode) {
                (this as any)[`_${drawType}`](gl, count, type);
            }
        }
        this.reset(gl);
    }

    public reset(gl: any) {
        gl.bindTexture(gl.TEXTURE_2D, null);
    }


    public addTexure(gl: any, key: string, data: any) {
        this.mat!.addMap(gl, key, data);
    }

    public addTexureNew(gl: any, data: any) {
        this.mat!.addMapNew(gl, data);
    }

    private _drawArrays(gl: any, count: number, mode: any) {
        gl.drawArrays(gl[mode], 0, count);
    }

    private _drawElements(gl: any, count: number, mode: any) {
        gl.drawElements(gl[mode], count, gl.UNSIGNED_SHORT, 0);
        // gl.drawElements(gl[mode], count, gl.UNSIGNED_BYTE, 0);//BYTE只能用于Uint8Array的elements绘制
    }

    public clone(name: string) {
        return new Mesh(name, {
            mat: this.mat!.clone(),
            geo: this.geo!.clone(),
        });
    }

}